'use strict'

const assert = require('node:assert/strict')

const { expect } = require('chai')
const sinon = require('sinon')
const { addVulnerability, sendVulnerabilities, clearCache, start, stop } =
  require('../../../src/appsec/iast/vulnerability-reporter')
const VulnerabilityAnalyzer = require('../../../../dd-trace/src/appsec/iast/analyzers/vulnerability-analyzer')
const { USER_KEEP } = require('../../../../../ext/priority')
const { ASM } = require('../../../src/standalone/product')

describe('vulnerability-reporter', () => {
  let vulnerabilityAnalyzer

  beforeEach(() => {
    clearCache()
    vulnerabilityAnalyzer = new VulnerabilityAnalyzer('ANALYZER_TYPE')
  })

  afterEach(sinon.restore)

  describe('addVulnerability', () => {
    it('should not break with invalid input', () => {
      addVulnerability()
      addVulnerability(null, null)
      addVulnerability(undefined, undefined)
      addVulnerability(null, {})
      addVulnerability({}, null)
      addVulnerability({}, undefined)
      addVulnerability({})
    })

    describe('with rootSpan', () => {
      let iastContext = {
        rootSpan: {}
      }

      afterEach(() => {
        iastContext = {
          rootSpan: {}
        }
      })

      it('should not add null vulnerability', () => {
        addVulnerability(iastContext, null)
        assert.ok(!Object.hasOwn(iastContext, 'vulnerabilities'))
        iastContext.vulnerabilities = []
        addVulnerability(iastContext, undefined)
        assert.strictEqual(iastContext.vulnerabilities.length, 0)
      })

      it('should create vulnerability array if it does not exist', () => {
        addVulnerability(iastContext,
          vulnerabilityAnalyzer._createVulnerability('INSECURE_HASHING', { value: 'sha1' }, 888), [])
        assert.ok(Object.hasOwn(iastContext, 'vulnerabilities'))
        assert.ok(Array.isArray(iastContext.vulnerabilities))
      })

      it('should deduplicate same vulnerabilities', () => {
        addVulnerability(iastContext,
          vulnerabilityAnalyzer._createVulnerability('INSECURE_HASHING', { value: 'sha1' }, -555), [])
        addVulnerability(iastContext,
          vulnerabilityAnalyzer._createVulnerability('INSECURE_HASHING', { value: 'sha1' }, 888), [])
        addVulnerability(iastContext,
          vulnerabilityAnalyzer._createVulnerability('INSECURE_HASHING', { value: 'sha1' }, 123), [])
        assert.strictEqual(iastContext.vulnerabilities.length, 1)
      })

      it('should add in the context evidence properties', () => {
        addVulnerability(iastContext,
          vulnerabilityAnalyzer._createVulnerability('INSECURE_HASHING', { value: 'sha1' }, 888), [])
        addVulnerability(iastContext,
          vulnerabilityAnalyzer._createVulnerability('INSECURE_HASHING', { value: 'md5' },
            -123, { path: 'path.js', line: 12 }), [])
        assert.strictEqual(iastContext.vulnerabilities.length, 2)
        expect(iastContext).to.have.nested.property('vulnerabilities.0.type', 'INSECURE_HASHING')
        expect(iastContext).to.have.nested.property('vulnerabilities.0.evidence.value', 'sha1')
        expect(iastContext).to.have.nested.property('vulnerabilities.0.location.spanId', 888)
        expect(iastContext).to.have.nested.property('vulnerabilities.1.type', 'INSECURE_HASHING')
        expect(iastContext).to.have.nested.property('vulnerabilities.1.evidence.value', 'md5')
        expect(iastContext).to.have.nested.property('vulnerabilities.1.location.spanId', -123)
        expect(iastContext).to.have.nested.property('vulnerabilities.1.location.path', 'path.js')
        expect(iastContext).to.have.nested.property('vulnerabilities.1.location.line', 12)
      })
    })

    describe('without rootSpan', () => {
      let fakeTracer
      let onTheFlySpan
      let prioritySampler

      beforeEach(() => {
        prioritySampler = {
          setPriority: sinon.stub()
        }
        onTheFlySpan = {
          _prioritySampler: prioritySampler,
          finish: sinon.spy(),
          addTags: sinon.spy(),
          context () {
            return {
              toSpanId () {
                return 42
              }
            }
          }
        }
        fakeTracer = {
          startSpan: sinon.stub().returns(onTheFlySpan)
        }
        start({
          iast: {
            deduplicationEnabled: true,
            stackTrace: {
              enabled: true
            }
          },
          appsec: {
            stackTrace: {
              enabled: true,
              maxStackTraces: 2,
              maxDepth: 42
            }
          }
        }, fakeTracer)
      })

      afterEach(() => {
        sinon.restore()
        stop()
      })

      it('should create span on the fly', () => {
        const vulnerability =
          vulnerabilityAnalyzer._createVulnerability('INSECURE_HASHING', { value: 'sha1' }, undefined,
            { path: 'filename.js', line: 73 }, '1')
        addVulnerability(undefined, vulnerability, [])
        sinon.assert.calledOnceWithExactly(fakeTracer.startSpan, 'vulnerability', { type: 'vulnerability' })
        sinon.assert.calledWithExactly(onTheFlySpan.addTags.firstCall, {
          '_dd.iast.enabled': 1
        })
        sinon.assert.calledWithExactly(onTheFlySpan.addTags.secondCall, {
          '_dd.iast.json': '{"sources":[],"vulnerabilities":[{"type":"INSECURE_HASHING","hash":3410512655,' +
          '"evidence":{"value":"sha1"},"location":{"spanId":42,"stackId":"1","path":"filename.js","line":73}}]}'
        })
        expect(prioritySampler.setPriority)
          .to.have.been.calledOnceWithExactly(onTheFlySpan, USER_KEEP, ASM)
        sinon.assert.calledOnce(onTheFlySpan.finish)
      })

      it('should update spanId in vulnerability\'s location with the new id from created span', () => {
        const vulnerability =
          vulnerabilityAnalyzer._createVulnerability('INSECURE_HASHING', { value: 'sha1' }, undefined,
            { path: 'filename.js', line: 73 })
        addVulnerability(undefined, vulnerability, [])
        assert.strictEqual(vulnerability.location.spanId, 42)
      })
    })
  })

  describe('with maxStackTraces limit', () => {
    let iastContext, vulnerability, callSiteFrames

    beforeEach(() => {
      iastContext = {
        rootSpan: {
          meta_struct: {
            '_dd.stack': {}
          }
        }
      }
      vulnerability = vulnerabilityAnalyzer._createVulnerability(
        'INSECURE_HASHING',
        { value: 'sha1' },
        888,
        { path: 'test.js', line: 1 }
      )
      callSiteFrames = [{
        getFileName: () => 'test.js',
        getLineNumber: () => 1
      }]
    })

    afterEach(() => {
      stop()
    })

    it('should report stack trace when under maxStackTraces limit', () => {
      start({
        iast: {
          deduplicationEnabled: true,
          stackTrace: {
            enabled: true
          }
        },
        appsec: {
          stackTrace: {
            enabled: true,
            maxStackTraces: 2,
            maxDepth: 42
          }
        }
      })
      addVulnerability(iastContext, vulnerability, callSiteFrames)

      assert.strictEqual(iastContext.rootSpan.meta_struct['_dd.stack'].vulnerability.length, 1)
    })

    it('should not report stack trace when at maxStackTraces limit', () => {
      start({
        iast: {
          deduplicationEnabled: true,
          stackTrace: {
            enabled: true
          }
        },
        appsec: {
          stackTrace: {
            enabled: true,
            maxStackTraces: 1,
            maxDepth: 42
          }
        }
      })
      iastContext.rootSpan.meta_struct['_dd.stack'].vulnerability = ['existing_stack']

      addVulnerability(iastContext, vulnerability, callSiteFrames)

      assert.strictEqual(iastContext.rootSpan.meta_struct['_dd.stack'].vulnerability.length, 1)
      assert.strictEqual(iastContext.rootSpan.meta_struct['_dd.stack'].vulnerability[0], 'existing_stack')
    })

    it('should always report stack trace when maxStackTraces is 0', () => {
      start({
        iast: {
          deduplicationEnabled: true,
          stackTrace: {
            enabled: true
          }
        },
        appsec: {
          stackTrace: {
            enabled: true,
            maxStackTraces: 0,
            maxDepth: 42
          }
        }
      })
      iastContext.rootSpan.meta_struct['_dd.stack'].vulnerability = ['stack1', 'stack2']

      addVulnerability(iastContext, vulnerability, callSiteFrames)

      assert.strictEqual(iastContext.rootSpan.meta_struct['_dd.stack'].vulnerability.length, 3)
    })
  })

  describe('sendVulnerabilities', () => {
    let span
    let context
    let prioritySampler

    beforeEach(() => {
      context = { _trace: { tags: {} } }
      prioritySampler = {
        setPriority: sinon.stub()
      }
      span = {
        _prioritySampler: prioritySampler,
        addTags: sinon.stub(),
        context: sinon.stub().returns(context)
      }
      start({
        iast: {
          deduplicationEnabled: true,
          stackTrace: {
            enabled: true
          }
        },
        appsec: {
          stackTrace: {
            enabled: true,
            maxStackTraces: 2,
            maxDepth: 42
          }
        }
      })
    })

    afterEach(() => {
      sinon.restore()
      stop()
    })

    it('should not fail with invalid parameters', () => {
      sendVulnerabilities()
      sendVulnerabilities(null)
      sendVulnerabilities(undefined)
      sendVulnerabilities(-15)
      sendVulnerabilities({})
    })

    it('should not send invalid vulnerability', () => {
      sendVulnerabilities([{ invalid: 'vulnerability' }], span)
      sinon.assert.notCalled(span.addTags)
    })

    it('should send one with one vulnerability', () => {
      const iastContext = { rootSpan: span }
      addVulnerability(iastContext,
        vulnerabilityAnalyzer._createVulnerability('INSECURE_HASHING', { value: 'sha1' }, 888), [])
      sendVulnerabilities(iastContext.vulnerabilities, span)
      sinon.assert.calledOnceWithExactly(span.addTags, {
        '_dd.iast.json': '{"sources":[],"vulnerabilities":[{"type":"INSECURE_HASHING","hash":3254801297,' +
          '"evidence":{"value":"sha1"},"location":{"spanId":888}}]}'
      })
      sinon.assert.calledOnceWithExactly(prioritySampler.setPriority, span, USER_KEEP, ASM)
    })

    it('should send only valid vulnerabilities', () => {
      const iastContext = { rootSpan: span }
      addVulnerability(iastContext,
        vulnerabilityAnalyzer._createVulnerability('INSECURE_HASHING', { value: 'sha1' }, 888), [])
      iastContext.vulnerabilities.push({ invalid: 'vulnerability' })
      sendVulnerabilities(iastContext.vulnerabilities, span)
      sinon.assert.calledOnceWithExactly(span.addTags, {
        '_dd.iast.json': '{"sources":[],"vulnerabilities":[{"type":"INSECURE_HASHING","hash":3254801297,' +
          '"evidence":{"value":"sha1"},"location":{"spanId":888}}]}'
      })
      sinon.assert.calledOnceWithExactly(prioritySampler.setPriority, span, USER_KEEP, ASM)
    })

    it('should send vulnerabilities with evidence, ranges and sources', () => {
      const iastContext = { rootSpan: span }
      const evidence1 = {
        value: 'SELECT * FROM u WHERE name = \'joe\';',
        ranges: [
          {
            start: 30,
            end: 33,
            iinfo: {
              type: 'ORIGIN_TYPE_1',
              parameterName: 'PARAMETER_NAME_1',
              parameterValue: 'joe'
            }
          }
        ]
      }
      addVulnerability(
        iastContext,
        vulnerabilityAnalyzer._createVulnerability('SQL_INJECTION', evidence1, 888, { path: 'filename.js', line: 88 }),
        []
      )

      const evidence2 = {
        value: 'SELECT id FROM u WHERE email = \'joe@mail.com\';',
        ranges: [
          {
            start: 32,
            end: 44,
            iinfo: {
              type: 'ORIGIN_TYPE_2',
              parameterName: 'PARAMETER_NAME_2',
              parameterValue: 'joe@mail.com'
            }
          }
        ]
      }
      addVulnerability(
        iastContext,
        vulnerabilityAnalyzer._createVulnerability('SQL_INJECTION', evidence2, 888, { path: 'filename.js', line: 99 }),
        []
      )

      sendVulnerabilities(iastContext.vulnerabilities, span)
      sinon.assert.calledOnceWithExactly(span.addTags, {
        '_dd.iast.json': '{"sources":[{"origin":"ORIGIN_TYPE_1","name":"PARAMETER_NAME_1","value":"joe"},' +
          '{"origin":"ORIGIN_TYPE_2","name":"PARAMETER_NAME_2","value":"joe@mail.com"}],' +
          '"vulnerabilities":[{"type":"SQL_INJECTION","hash":4676753086,' +
          '"evidence":{"valueParts":[{"value":"SELECT * FROM u WHERE name = \'"},{"value":"joe","source":0},' +
          '{"value":"\';"}]},"location":{"spanId":888,"path":"filename.js","line":88}},' +
          '{"type":"SQL_INJECTION","hash":4676753118,"evidence":{"valueParts":' +
          '[{"value":"SELECT id FROM u WHERE email = \'"},{"value":"joe@mail.com","source":1},{"value":"\';"}]},' +
          '"location":{"spanId":888,"path":"filename.js","line":99}}]}'
      })

      sinon.assert.calledTwice(prioritySampler.setPriority)
      expect(prioritySampler.setPriority.firstCall)
        .to.have.been.calledWithExactly(span, USER_KEEP, ASM)
      expect(prioritySampler.setPriority.secondCall)
        .to.have.been.calledWithExactly(span, USER_KEEP, ASM)
    })

    it('should send multiple vulnerabilities with same tainted source', () => {
      const iastContext = { rootSpan: span }
      const evidence1 = {
        value: 'SELECT * FROM u WHERE name = \'joe\';',
        ranges: [
          {
            start: 30,
            end: 33,
            iinfo: {
              type: 'ORIGIN_TYPE_1',
              parameterName: 'PARAMETER_NAME_1',
              parameterValue: 'joe'
            }
          }
        ]
      }
      addVulnerability(
        iastContext,
        vulnerabilityAnalyzer._createVulnerability('SQL_INJECTION', evidence1, 888, { path: 'filename.js', line: 88 }),
        []
      )

      const evidence2 = {
        value: 'UPDATE u SET name=\'joe\' WHERE id=1;',
        ranges: [
          {
            start: 19,
            end: 22,
            iinfo: {
              type: 'ORIGIN_TYPE_1',
              parameterName: 'PARAMETER_NAME_1',
              parameterValue: 'joe'
            }
          }
        ]
      }
      addVulnerability(
        iastContext,
        vulnerabilityAnalyzer._createVulnerability('SQL_INJECTION', evidence2, 888, { path: 'filename.js', line: 99 }),
        []
      )

      sendVulnerabilities(iastContext.vulnerabilities, span)
      sinon.assert.calledOnceWithExactly(span.addTags, {
        '_dd.iast.json': '{"sources":[{"origin":"ORIGIN_TYPE_1","name":"PARAMETER_NAME_1","value":"joe"}],' +
          '"vulnerabilities":[{"type":"SQL_INJECTION","hash":4676753086,' +
          '"evidence":{"valueParts":[{"value":"SELECT * FROM u WHERE name = \'"},{"value":"joe","source":0},' +
          '{"value":"\';"}]},"location":{"spanId":888,"path":"filename.js","line":88}},' +
          '{"type":"SQL_INJECTION","hash":4676753118,"evidence":{"valueParts":' +
          '[{"value":"UPDATE u SET name=\'"},{"value":"joe","source":0},{"value":"\' WHERE id=1;"}]},' +
          '"location":{"spanId":888,"path":"filename.js","line":99}}]}'
      })

      sinon.assert.calledTwice(prioritySampler.setPriority)
      expect(prioritySampler.setPriority.firstCall)
        .to.have.been.calledWithExactly(span, USER_KEEP, ASM)
      expect(prioritySampler.setPriority.secondCall)
        .to.have.been.calledWithExactly(span, USER_KEEP, ASM)
    })

    it('should send once with multiple vulnerabilities', () => {
      const iastContext = { rootSpan: span }
      addVulnerability(iastContext, vulnerabilityAnalyzer._createVulnerability('INSECURE_HASHING', { value: 'sha1' },
        888, { path: '/path/to/file1.js', line: 1 }), [])
      addVulnerability(iastContext, vulnerabilityAnalyzer._createVulnerability('INSECURE_HASHING', { value: 'md5' }, 1,
        { path: '/path/to/file2.js', line: 1 }), [])
      addVulnerability(iastContext, vulnerabilityAnalyzer._createVulnerability('INSECURE_HASHING', { value: 'md5' }, -5,
        { path: '/path/to/file3.js', line: 3 }), [])
      sendVulnerabilities(iastContext.vulnerabilities, span)
      sinon.assert.calledOnceWithExactly(span.addTags, {
        '_dd.iast.json': '{"sources":[],"vulnerabilities":[' +
          '{"type":"INSECURE_HASHING","hash":1697980169,"evidence":{"value":"sha1"},' +
            '"location":{"spanId":888,"path":"/path/to/file1.js","line":1}},' +
          '{"type":"INSECURE_HASHING","hash":1726609320,"evidence":{"value":"md5"},' +
            '"location":{"spanId":1,"path":"/path/to/file2.js","line":1}},' +
          '{"type":"INSECURE_HASHING","hash":1755238473,"evidence":{"value":"md5"},' +
            '"location":{"spanId":-5,"path":"/path/to/file3.js","line":3}}]}'
      })
      sinon.assert.calledThrice(prioritySampler.setPriority)
      expect(prioritySampler.setPriority.firstCall)
        .to.have.been.calledWithExactly(span, USER_KEEP, ASM)
      expect(prioritySampler.setPriority.secondCall)
        .to.have.been.calledWithExactly(span, USER_KEEP, ASM)
      expect(prioritySampler.setPriority.thirdCall)
        .to.have.been.calledWithExactly(span, USER_KEEP, ASM)
    })

    it('should send once vulnerability with one vulnerability', () => {
      const iastContext = { rootSpan: span }
      addVulnerability(iastContext,
        vulnerabilityAnalyzer._createVulnerability('INSECURE_HASHING', { value: 'sha1' }, 888,
          { path: 'filename.js', line: 88 }), [])
      sendVulnerabilities(iastContext.vulnerabilities, span)
      sinon.assert.calledOnceWithExactly(span.addTags, {
        '_dd.iast.json': '{"sources":[],"vulnerabilities":[{"type":"INSECURE_HASHING","hash":3410512691,' +
          '"evidence":{"value":"sha1"},"location":{"spanId":888,"path":"filename.js","line":88}}]}'
      })
      sinon.assert.calledOnceWithExactly(prioritySampler.setPriority, span, USER_KEEP, ASM)
    })

    it('should not send duplicated vulnerabilities', () => {
      const iastContext = { rootSpan: span }
      addVulnerability(iastContext,
        vulnerabilityAnalyzer._createVulnerability('INSECURE_HASHING', { value: 'sha1' }, 888,
          { path: 'filename.js', line: 88 }), [])
      addVulnerability(iastContext,
        vulnerabilityAnalyzer._createVulnerability('INSECURE_HASHING', { value: 'sha1' }, 888,
          { path: 'filename.js', line: 88 }), [])
      sendVulnerabilities(iastContext.vulnerabilities, span)
      sinon.assert.calledOnceWithExactly(span.addTags, {
        '_dd.iast.json': '{"sources":[],"vulnerabilities":[{"type":"INSECURE_HASHING","hash":3410512691,' +
          '"evidence":{"value":"sha1"},"location":{"spanId":888,"path":"filename.js","line":88}}]}'
      })
      sinon.assert.calledOnceWithExactly(prioritySampler.setPriority, span, USER_KEEP, ASM)
    })

    it('should not deduplicate vulnerabilities if not enabled', () => {
      start({
        iast: {
          deduplicationEnabled: false,
          stackTrace: {
            enabled: true
          }
        },
        appsec: {
          stackTrace: {
            enabled: true,
            maxStackTraces: 2,
            maxDepth: 42
          }
        }
      })
      const iastContext = { rootSpan: span }
      addVulnerability(iastContext, vulnerabilityAnalyzer._createVulnerability('INSECURE_HASHING',
        { value: 'sha1' }, 888, { path: 'filename.js', line: 88 }), [])
      addVulnerability(iastContext, vulnerabilityAnalyzer._createVulnerability('INSECURE_HASHING',
        { value: 'sha1' }, 888, { path: 'filename.js', line: 88 }), [])
      sendVulnerabilities(iastContext.vulnerabilities, span)
      sinon.assert.calledOnceWithExactly(span.addTags, {
        '_dd.iast.json': '{"sources":[],"vulnerabilities":[{"type":"INSECURE_HASHING","hash":3410512691,' +
          '"evidence":{"value":"sha1"},"location":{"spanId":888,"path":"filename.js","line":88}},' +
          '{"type":"INSECURE_HASHING","hash":3410512691,"evidence":{"value":"sha1"},"location":' +
          '{"spanId":888,"path":"filename.js","line":88}}]}'
      })
      sinon.assert.calledTwice(prioritySampler.setPriority)
      expect(prioritySampler.setPriority.firstCall)
        .to.have.been.calledWithExactly(span, USER_KEEP, ASM)
      expect(prioritySampler.setPriority.secondCall)
        .to.have.been.calledWithExactly(span, USER_KEEP, ASM)
    })
  })

  describe('clearCache', () => {
    let span
    let originalSetInterval
    let originalClearInterval

    before(() => {
      originalSetInterval = global.setInterval
      originalClearInterval = global.clearInterval
    })

    beforeEach(() => {
      global.setInterval = sinon.spy(global.setInterval)
      global.clearInterval = sinon.spy(global.clearInterval)
      span = {
        addTags: sinon.stub(),
        keep: sinon.stub()
      }
    })

    after(() => {
      global.setInterval = originalSetInterval
      global.clearInterval = originalClearInterval
    })

    afterEach(() => {
      sinon.restore()
    })

    it('should empty the cache with more than 1000 hashes', () => {
      const iastContext = { rootSpan: span }
      const MAX = 1000
      const vulnerabilityToRepeatInTheNext =
        vulnerabilityAnalyzer._createVulnerability('INSECURE_HASHING', { value: 'sha1' }, 888,
          { path: 'filename.js', line: 0 }, 1)
      addVulnerability(iastContext, vulnerabilityToRepeatInTheNext, [])
      for (let i = 1; i <= MAX; i++) {
        addVulnerability(iastContext,
          vulnerabilityAnalyzer._createVulnerability('INSECURE_HASHING', { value: 'sha1' }, 888,
            { path: 'filename.js', line: i }), [])
      }
      sendVulnerabilities(iastContext.vulnerabilities, span)
      sinon.assert.calledOnce(span.addTags)

      const nextIastContext = { rootSpan: span }
      addVulnerability(nextIastContext, vulnerabilityToRepeatInTheNext, [])
      sendVulnerabilities(nextIastContext.vulnerabilities, span)
      sinon.assert.calledTwice(span.addTags)
    })

    it('should set timer to clear cache every hour if deduplication is enabled', () => {
      const config = {
        iast: {
          deduplicationEnabled: true,
          stackTrace: {
            enabled: true
          }
        },
        appsec: {
          stackTrace: {
            enabled: true,
            maxStackTraces: 2,
            maxDepth: 42
          }
        }
      }
      start(config)
      sinon.assert.calledOnceWithExactly(global.setInterval, clearCache, 60 * 60 * 1000)
    })

    it('should not set timer to clear cache every hour if deduplication is not enabled', () => {
      const config = {
        iast: {
          deduplicationEnabled: false,
          stackTrace: {
            enabled: true
          }
        },
        appsec: {
          stackTrace: {
            enabled: true,
            maxStackTraces: 2,
            maxDepth: 42
          }
        }
      }
      start(config)
      sinon.assert.notCalled(global.setInterval)
    })

    it('should unset timer to clear cache every hour', () => {
      const config = {
        iast: {
          deduplicationEnabled: true,
          stackTrace: {
            enabled: true
          }
        },
        appsec: {
          stackTrace: {
            enabled: true,
            maxStackTraces: 2,
            maxDepth: 42
          }
        }
      }
      start(config)
      stop()
      sinon.assert.calledOnce(global.clearInterval)
    })
  })

  describe('evidence redaction', () => {
    const vulnerabilityFormatter = require('../../../src/appsec/iast/vulnerabilities-formatter')

    beforeEach(() => {
      sinon.spy(vulnerabilityFormatter, 'setRedactVulnerabilities')
    })

    afterEach(() => {
      sinon.restore()
    })

    it('should set evidence redaction on vulnerability formatter', () => {
      const config = {
        iast: {
          redactionEnabled: true,
          redactionNamePattern: null,
          redactionValuePattern: null,
          stackTrace: {
            enabled: true
          }
        },
        appsec: {
          stackTrace: {
            enabled: true,
            maxStackTraces: 2,
            maxDepth: 42
          }
        }
      }
      start(config)
      sinon.assert.calledOnceWithExactly(vulnerabilityFormatter.setRedactVulnerabilities, true, null, null)
    })
  })
})
